package cloud.hedou.abp.starter

import cloud.hedou.abp.asset.RemoteAssetService
import cloud.hedou.abp.auth.AbpGrantedAuthoritiesConverter
import cloud.hedou.abp.auth.AbpSecurityConfiguration
import cloud.hedou.abp.auth.AbpWebMvcConfigurer
import cloud.hedou.abp.common.RemoteCommonService
import cloud.hedou.abp.device.DeviceRemoteService
import cloud.hedou.abp.evaluate.DeviceEvaluateService
import cloud.hedou.abp.identity.RemoteIdentityService
import cloud.hedou.abp.message.IdentityRegisterRunner
import cloud.hedou.abp.message.RemoteMessageService
import cloud.hedou.abp.post.RemotePostService
import cloud.hedou.abp.remote.AbpHttpClient
import cloud.hedou.abp.remote.HttpClient
import cloud.hedou.abp.schedule.RemoteScheduleService
import cloud.hedou.abp.todo.RemoteTodoService
import cloud.hedou.abp.warehouse.RemoteWarehouseService
import cloud.hedou.abp.workorder.RemoteWorkOrderService
import com.fasterxml.jackson.databind.ObjectMapper
import com.nimbusds.jose.JOSEObjectType
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.AutoConfigureAfter
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.cache.Cache
import org.springframework.cache.CacheManager
import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.retry.annotation.EnableRetry
import org.springframework.scheduling.annotation.EnableAsync
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver
import org.springframework.web.client.RestTemplate

@EnableAsync
@EnableRetry
@EnableCaching
@Import(AbpSecurityConfiguration::class, AbpWebMvcConfigurer::class, AbpContext::class, IdentityRegisterRunner::class)
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(JacksonAutoConfiguration::class)
@EnableConfigurationProperties(Server::class, Auth::class)
class AbpAutoConfiguration {

    @Bean
    fun abpHttpClient(server: Server, objectMapper: ObjectMapper): HttpClient {
        return AbpHttpClient(server, objectMapper)
    }

    @Bean
    fun okHttpClient(httpClient: HttpClient): OkHttpClient {
        return (httpClient as AbpHttpClient).okHttpClient
    }

    @Bean
    fun remoteIdentityService(httpClient: HttpClient): RemoteIdentityService {
        return RemoteIdentityService(httpClient)
    }

    @Bean
    fun remoteCommonService(httpClient: HttpClient): RemoteCommonService {
        return RemoteCommonService(httpClient)
    }

    @Bean
    fun remoteMessageService(httpClient: HttpClient): RemoteMessageService {
        return RemoteMessageService(httpClient)
    }

    @Bean
    fun deviceEvaluateService(httpClient: HttpClient): DeviceEvaluateService {
        return DeviceEvaluateService(httpClient)
    }

    @Bean
    fun remoteWarehouseService(httpClient: HttpClient): RemoteWarehouseService {
        return RemoteWarehouseService(httpClient)
    }

    @Bean
    fun remoteScheduleService(httpClient: HttpClient): RemoteScheduleService {
        return RemoteScheduleService(httpClient)
    }

    @Bean
    fun remoteWorkOrderService(httpClient: HttpClient): RemoteWorkOrderService {
        return RemoteWorkOrderService(httpClient)
    }

    @Bean
    fun remoteTodoService(httpClient: HttpClient): RemoteTodoService {
        return RemoteTodoService(httpClient)
    }

    @Bean
    fun remoteAssetService(httpClient: HttpClient): RemoteAssetService {
        return RemoteAssetService(httpClient)
    }

    @Bean
    fun remoteDeviceService(httpClient: HttpClient): DeviceRemoteService {
        return DeviceRemoteService(httpClient)
    }

    @Bean
    fun remotePostService(httpClient: HttpClient): RemotePostService {
        return RemotePostService(httpClient)
    }

    @Bean
    fun jwksCache(cacheManager: CacheManager): Cache {
        return cacheManager.getCache("jwks")!!
    }

    @Bean
    fun abpJwtDecoder(
        @Value("\${abp.server.oauth}") serverUrl: String,
        jwksCache: Cache,
        restTemplate: RestTemplate,
        ): JwtDecoder {
        // 初始化jwks地址
        val jwkUri = HttpUrl.get(serverUrl)
            .newBuilder()
            .encodedPath("/.well-known/jwks")
            .build()
            .toString()
        return NimbusJwtDecoder
            .withJwkSetUri(jwkUri)
            .cache(jwksCache)
            .restOperations(restTemplate)
            .jwtProcessorCustomizer {
                it.setJWSTypeVerifier(DefaultJOSEObjectTypeVerifier(JOSEObjectType("at+jwt")))
            }
            .build()
    }

    @Bean
    @ConditionalOnMissingBean
    fun abpBearerTokenResolver(): DefaultBearerTokenResolver {
        val bearerTokenResolver = DefaultBearerTokenResolver()
        bearerTokenResolver.setAllowUriQueryParameter(true)
        bearerTokenResolver.setAllowFormEncodedBodyParameter(true)
        return bearerTokenResolver
    }

    @Bean
    @ConditionalOnMissingBean
    fun abpGrantedAuthoritiesConverter(remoteIdentityService: RemoteIdentityService): AbpGrantedAuthoritiesConverter {
        return AbpGrantedAuthoritiesConverter(remoteIdentityService)
    }

}